﻿using System;
using System.Linq;
using Grinder.Renamer;
using System.IO;
using NDesk.Options;

namespace grinder_console
{
    class Program
    {
        private static readonly string ExecutableName = Path.GetFileName(Environment.GetCommandLineArgs()[0]);

        static void Main(string[] args)
        {
            FileRenamer fr = new FileRenamer();

            var template = "";
            var mode = ProcessingMode.Copy;
            var input = "";
            var output = "";
            var counterStart = 1;
            var counterStep = 1;
            var recurse = false;
            var overwrite = false;
            var simulate = false;
            var silent = false;
            var showHelp = false;
            var filter = "*";

            #region Options
            var optionSet = new OptionSet {                
                    {"t|template=", 
                        "File name template\n",
                        v=> template = v},

                    { "m|mode=",
                        "File process mode, allowed values: move, copy and rename (default=copy)\n",
                        (ProcessingMode v) => mode = v },

                    { "i|input=",
                        "Input directory, if not specified current directory will be used\n",
                        v => input = v },

                    { "o|output=",
                        "Output directory, must be specified when copying or moving files\n",
                        v => output = v },

                    {"r|recurse","Recurse into sub-folders\n" , v => recurse  = v != null},
                    
                    {"f|filter=","File filter (default is *)\n" , v => filter  = v},

                    {"w|overwrite","Overwrite existing files\n" , v => overwrite = v != null},                    

                    {"counter-start=","Start counter at (must be integer)\n" , (int v) => counterStart = v},

                    {"counter-step=","Counter step (must be integer)\n" , (int v) => counterStep = v},

                    {"s|simulate", "Simulation mode. Does not process files\n" , v => simulate = v != null},

                    {"l|silent", "Do not show overview and do not ask questions\n" , v => silent = v != null},

                    {"h|?|help","Show this help message" , v => showHelp  = v != null},                    
                };
            #endregion

            try
            {
                optionSet.Parse(args);
            }
            catch (OptionException opex)
            {
                ShowErrorMessage(opex.Message);
                return;
            }

            if (showHelp)
            {
                ShowHelpMessage(optionSet);
                return;
            }

            if (string.IsNullOrEmpty(template))
            {
                ShowErrorMessage("Please specify file name template.");
                return;
            }

            #region Check input directory
            if (string.IsNullOrEmpty(input))
                input = Environment.CurrentDirectory;

            DirectoryInfo inputDirInfo;
            try
            {
                inputDirInfo = new DirectoryInfo(input);
            }
            catch (ArgumentException)
            {
                ShowErrorMessage("Input path is not of a legal form.");
                return;
            }

            if (!inputDirInfo.Exists)
            {
                ShowErrorMessage("Specified input directory does not exist");
                return;
            }

            #endregion

            #region Check output directory

            if (string.IsNullOrEmpty(output) && (mode == ProcessingMode.Copy || mode == ProcessingMode.Move))
            {
                ShowErrorMessage("Output folder not specified for copy/move mode");
                return;
            }


            #endregion

            FileInfo[] files = inputDirInfo.GetFiles(filter, recurse ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);

            if (files.Count() == 0)
            {
                Console.WriteLine("No files matching filter \"{0}\" found in directory \"{1}\"", filter, inputDirInfo.FullName);
                return;
            }

            var fileRenamer = new FileRenamer();

            const int padto = 30;

            if (!silent)
            {
                Console.WriteLine("=========================================================================");
                Console.WriteLine("  Template: ".PadRight(padto) + template);
                Console.WriteLine("  File mode: ".PadRight(padto) + mode);
                Console.WriteLine("  Input directory: ".PadRight(padto) + input);
                Console.WriteLine("  Output directory: ".PadRight(padto) + output);
                Console.WriteLine("  Files filter: ".PadRight(padto) + filter);
                Console.WriteLine("  Overwrite files: ".PadRight(padto) + (overwrite ? "yes" : "no"));
                Console.WriteLine("  Recurse sub-folders: ".PadRight(padto) + (recurse ? "yes" : "no"));
                Console.WriteLine("  Counter start: ".PadRight(padto) + counterStart);
                Console.WriteLine("  Counter step: ".PadRight(padto) + counterStep);
                Console.WriteLine("  Files to process: ".PadRight(padto) + files.Count());
                Console.WriteLine("=========================================================================");
                if (simulate)
                    Console.WriteLine("\n!!! Simulating, no files are being actually processed !!!\n");

                Console.Write("Continue? (y/n): ");

                string answer = Console.In.ReadLine();
                if (answer.ToUpper() != "Y")
                    return;

                Console.WriteLine();
            }


            int counter = counterStart;

            string modeStr = "";
            if (mode == ProcessingMode.Copy)
                modeStr = "Copying ";
            else if (mode == ProcessingMode.Rename)
                modeStr = "Renaming ";
            else if (mode == ProcessingMode.Move)
                modeStr = "Moving ";

            foreach (var file in files)
            {
                string newFileName = fileRenamer.GetNewFilename(template, file, counter);

                Console.WriteLine(modeStr + "file " + file.Name.PadRight(30) + " -> " + newFileName);

                if (!simulate)
                {
                    try
                    {
                        FileRenamer.ProcessFile(new ProcessFileParams(file, newFileName, mode, output, overwrite));
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("ERROR: Processing file " + file.Name + " -> " + ex.Message);
                        continue;
                    }
                }

                counter += counterStep;
            }
        }

        private static void ShowErrorMessage(string message)
        {
            Console.WriteLine();
            Console.WriteLine("ERROR: " + message);
            Console.WriteLine();
            Console.WriteLine("Try {0} --help for more information", ExecutableName);
            Console.WriteLine();
        }

        private static void ShowHelpMessage(OptionSet p)
        {
            string executable = Path.GetFileName(Environment.GetCommandLineArgs()[0]);

            Console.WriteLine("");
            Console.WriteLine("Usage: " + executable + " [options]");
            Console.WriteLine("");
            Console.WriteLine("Options:");
            p.WriteOptionDescriptions(Console.Out);
            Console.WriteLine("");
        }
    }
}
